using NUnit.Framework;
using System.Linq;
using StreetPacMan.Server.Facade;
using StreetPacMan.Server.Rules;

namespace StreetPacMan.Server.Tests.Integ
{
    [TestFixture]
    public class ApplesEating : BaseCleaner
    {
        private static readonly InitialApple AppleGeo =  new TestAppleLocationsProvider().GetInitialApples().First();

        [Test]
        public void DoServerPoll_AsPacMan_PollsInSameGeoOfApple_NotifyThatAppleRemoved()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));

            CollectionAssert.IsNotEmpty(retVal.RemovedAppleIds);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsNotInSameGeoOfApple_NoAppleRemoved()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, 0, 0, true));

            CollectionAssert.IsEmpty(retVal.RemovedAppleIds);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInSameGeoOfApple_NotifyThatApplesRemoveOnEnsuingCalls()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal1 = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));
            var retVal2 = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat + 1, AppleGeo.Lon + 1, true));

            CollectionAssert.IsNotEmpty(retVal2.RemovedAppleIds);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInDifferentGeoAsApple_NoNotifyOnRemovalOnEnsuingCalls()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal1 = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, 0, 0, true));
            var retVal2 = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, 1,1, true));

            CollectionAssert.IsEmpty(retVal2.RemovedAppleIds);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInSameGeoOfApple_MakeSureTheAppleDoesNotReturnOfCallsToGetAllApples()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);
            var initialAppleCount = instance.GetGameStateForX(pacman.PlayerId).Apples.Count();

            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));
            var postPollAppleCount = instance.GetGameStateForX(pacman.PlayerId).Apples.Count();

            Assert.AreNotEqual(initialAppleCount, postPollAppleCount);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInNonSameGeoOfApple_MakeSureAllTheAppleReturnInCallsToGetAllApples()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);
            var initialAppleCount = instance.GetGameStateForX(pacman.PlayerId).Apples.Count();

            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, 0,0, true));
            var postPollAppleCount = instance.GetGameStateForX(pacman.PlayerId).Apples.Count();

            Assert.AreEqual(initialAppleCount , postPollAppleCount);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInNonSameGeoOfApple_ScoreStaysSame()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);
            var earlyScore = instance.GetGameStateForX(pacman.PlayerId).Score;
          
            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, 0,0, true));
            var currentScore = instance.GetGameStateAsObvserverX("GameName").Score;
            Assert.AreEqual(earlyScore, currentScore);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInSameGeoOfApple_Add10PointsToScore()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);
            var earlyScore = instance.GetGameStateForX(pacman.PlayerId).Score;
        
            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));
            var currentScore = instance.GetGameStateAsObvserverX("GameName").Score;
            Assert.AreEqual(earlyScore + AddScoreForEatenApplesRule.PointsForEatingApple, currentScore);
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInSameGeoOfApple_EventForEatingAppleShouldBeAdded()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));
            var gameEvents = instance.GetAllEventsX("GameName");
            CollectionAssert.IsNotEmpty(gameEvents.Events.Where(x=>x.Type==EventType.PacManAteApple).ToArray());
        }

        [Test]
        public void DoServerPoll_AsPacMan_PollsInSameGeoOfApple_Twice_AddScoreAsIfAppleWasEatenOnlyOnce()
        {
            var instance = ServerFacadeFactory.GetServerFacade();

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);
            var earlyScore = instance.GetGameStateForX(pacman.PlayerId).Score;

            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));
            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, AppleGeo.Lat, AppleGeo.Lon, true));
            var currentScore = instance.GetGameStateAsObvserverX("GameName").Score;
            Assert.AreEqual(earlyScore + AddScoreForEatenApplesRule.PointsForEatingApple, currentScore);
        }

        [TestCase(AppleKind.Normal, AddScoreForEatenApplesRule.PointsForEatingApple)]
        [TestCase(AppleKind.Super, AddScoreForEatenApplesRule.PointsForEatingSuperApple)]
        [TestCase(AppleKind.Cherry, AddScoreForEatenApplesRule.PointsForEatingCherry)]
        public void DoServerPoll_AsPacMan_EatApple_AddTheCorrectScorePerAppleType(AppleKind kind, int points)
        {
            var singleAppleLocation = new InitialApple { Lat = 2, Lon = 2, Type = kind };
            var instance = ServerFacadeFactory.CreateGameWithApplesOnlyAt(singleAppleLocation, new InitialApple{Lat=3, Lon=3} );

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);
            var earlyScore = instance.GetGameStateForX(pacman.PlayerId).Score;

            instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, singleAppleLocation.Lat, singleAppleLocation.Lon, true));

            var currentScore = instance.GetGameStateAsObvserverX("GameName").Score;
            Assert.AreEqual(earlyScore + points, currentScore);
        }

        [Test]
        public void DoServerPoll_AsPacMan_EatTheLastAppleLeftOnBoard_ShouldSendMessageOfGameOverPacManWins()
        {
            var singleAppleLocation = new InitialApple { Lat = 2, Lon = 2,Type=AppleKind.Normal };
            var instance = ServerFacadeFactory.CreateGameWithApplesOnlyAt(singleAppleLocation);

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, singleAppleLocation.Lat, singleAppleLocation.Lon, true));

            Assert.AreEqual(
                string.Format("Game Over! PacMen win with {0} points!", AddScoreForEatenApplesRule.PointsForEatingApple),
                retVal.RecentMessages.Last().Body);
        }

        [Test]
        public void DoServerPoll_AsPacMan_EatTheLastSuperAppleLeftOnBoard_ShouldSendMessageOfGameOverPacManWins()
        {
            var singleAppleLocation = new InitialApple { Lat = 2, Lon = 2, Type = AppleKind.Super };
            var instance = ServerFacadeFactory.CreateGameWithApplesOnlyAt(singleAppleLocation);

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, singleAppleLocation.Lat, singleAppleLocation.Lon, true));

            Assert.AreEqual(
                string.Format("Game Over! PacMen win with {0} points!", AddScoreForEatenApplesRule.PointsForEatingSuperApple),
                retVal.RecentMessages.Last().Body);
        }


        [Test]
        public void DoServerPoll_AsPacMan_EatSuperApple_ShouldSendMessageThatPacManIsNowSuper()
        {
            var singleAppleLocation = new InitialApple { Lat = 2, Lon = 2, Type = AppleKind.Super };
            var instance = ServerFacadeFactory.CreateGameWithExtraApplesAt(singleAppleLocation);

            instance.CreateGameX("GameName");
            var pacman = instance.JoinGameX("GameName", "PacManName", PlayerRole.PacMan);

            var retVal = instance.UpdateMyWhereabouts(new ServerPollParameters(pacman.PlayerId, singleAppleLocation.Lat, singleAppleLocation.Lon, true));

            Assert.AreEqual("PacManName has eaten a power pellet!", retVal.RecentMessages.Last().Body);
            Assert.AreEqual(GameState.SuperPacMan, retVal.GameState);
        }
    }
}